﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;

namespace Vk.Methods.Photos
{
    /// <summary>
    ///     Приватность
    /// </summary>
    public class Privacy
    {
        private readonly ApiNode _node;

        /// <summary>
        ///     Идентификаторы списков пользователей. Если значение отрицательное, то доступ запрещен.
        /// </summary>
        public IEnumerable<int> ListIds => from x in _node.ChildNodes
                                           let str = x.String()
                                           where str != null && str.Contains("list")
                                           select int.Parse(str.Replace("list", string.Empty));

        /// <summary>
        /// </summary>
        public IEnumerable<AccessPrivacyType> Types
        {
            get
            {
                int parsed;
                return from x in _node.ChildNodes
                       let str = x.String()
                       where str != null && !str.Contains("list") && !int.TryParse(str, out parsed)
                       select AccessPrivacyType.Parse(str);
            }
        }

        /// <summary>
        ///     Идентификаторы пользователей. Если значение отрицательное, то доступ запрещен.
        /// </summary>
        public IEnumerable<int> UserIds => from x in _node.ChildNodes
                                           let num = x.Int()
                                           where num != null
                                           select num.Value;

        public Privacy(ApiNode node)
        {
            _node = node;
        }

        public Privacy(List<object> objects)
        {
            //Check objects
            if (objects.Any(o => o.GetType() != typeof(AccessPrivacyType) &&
                                 o.GetType() != typeof(string) &&
                                 o.GetType() != typeof(int)))
            {
                throw new Exception("Objects can be only integers, AccessPrivactType and strings");
            }
            if (objects.OfType<string>().Any(str => !Regex.IsMatch(str, "^-?list(\\d)+$")))
            {
                throw new Exception("Strings are used only for lists (-?list\\d+).");
            }


            _node = new ApiJsonNode(new JArray(objects.Select(x => x.ToString())));
        }

        public override string ToString()
        {
            return string.Join(",", _node.ChildNodes.Select(x => x.String()));
        }

        public static implicit operator string(Privacy privacy)
        {
            return privacy.ToString();
        }
    }

    /// <summary>
    ///     Тип приватности
    /// </summary>
    public class AccessPrivacyType : IEquatable<AccessPrivacyType>
    {
        public enum AccessPrivacyTypeEnum
        {
            /// <summary>
            ///     Ни один пользователь
            /// </summary>
            Nobody,

            /// <summary>
            ///     Доступно только мне
            /// </summary>
            OnlyMe,

            /// <summary>
            ///     Все пользователи
            /// </summary>
            All,

            /// <summary>
            ///     Только друзья
            /// </summary>
            Friends,

            /// <summary>
            ///     Друзья и друзья друзей
            /// </summary>
            FriendsOfFriends,

            /// <summary>
            ///     Только друзья друзей текущего пользователя
            /// </summary>
            FriendsOfFriendsOnly
        }

        public string StringValue { get; set; }

        public AccessPrivacyType(AccessPrivacyTypeEnum privacy)
        {
            switch (privacy)
            {
                case AccessPrivacyTypeEnum.All:
                    StringValue = "all";
                    break;
                case AccessPrivacyTypeEnum.Friends:
                    StringValue = "friends";
                    break;
                case AccessPrivacyTypeEnum.FriendsOfFriends:
                    StringValue = "friends_of_friends";
                    break;
                case AccessPrivacyTypeEnum.FriendsOfFriendsOnly:
                    StringValue = "friends_of_friends_only";
                    break;
                case AccessPrivacyTypeEnum.Nobody:
                    StringValue = "nobody";
                    break;
                case AccessPrivacyTypeEnum.OnlyMe:
                    StringValue = "only_me";
                    break;
            }
        }

        public bool Equals(AccessPrivacyType other)
        {
            if (ReferenceEquals(null, other))
                return false;
            if (ReferenceEquals(this, other))
                return true;
            return string.Equals(StringValue, other.StringValue);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
                return false;
            if (ReferenceEquals(this, obj))
                return true;
            if (obj.GetType() != GetType())
                return false;
            return Equals((AccessPrivacyType) obj);
        }

        public override int GetHashCode()
        {
            return StringValue != null ? StringValue.GetHashCode() : 0;
        }

        public static AccessPrivacyType Parse(string value)
        {
            switch (value)
            {
                case "all":
                    return new AccessPrivacyType(AccessPrivacyTypeEnum.All);
                case "friends":
                    return new AccessPrivacyType(AccessPrivacyTypeEnum.Friends);
                case "friends_of_friends":
                    return new AccessPrivacyType(AccessPrivacyTypeEnum.FriendsOfFriends);
                case "friends_of_friends_only":
                    return new AccessPrivacyType(AccessPrivacyTypeEnum.FriendsOfFriendsOnly);
                case "nobody":
                    return new AccessPrivacyType(AccessPrivacyTypeEnum.Nobody);
                case "only_me":
                    return new AccessPrivacyType(AccessPrivacyTypeEnum.OnlyMe);
                default:
                    throw new ArgumentException();
            }
        }

        public override string ToString()
        {
            return StringValue;
        }

        public static bool operator ==(AccessPrivacyType left, AccessPrivacyType right)
        {
            return Equals(left, right);
        }

        public static bool operator !=(AccessPrivacyType left, AccessPrivacyType right)
        {
            return !Equals(left, right);
        }
    }
}